/************************************************************************
*
*  LibTLib
*  Copyright (C) 2010  Thor Qin
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
*
* Author: Thor Qin
* Bug Report: thor.qin@gmail.com
*
**************************************************************************/

#include "tlibustr.h"
#include "tlibstr.h"


/* ustring class is very similar with Glib::ustring class,
 * and some code is from glibmm and glib
 */
namespace tlib
{

const ustring::size_type ustring::npos = std::string::npos;

#define UTF8_COMPUTE(Char, Mask, Len)				\
  if (Char < 128)							      	\
    {										      	\
      Len = 1;								      	\
      Mask = 0x7f;							      	\
    }									      		\
  else if ((Char & 0xe0) == 0xc0)					\
    {									      		\
      Len = 2;								      	\
      Mask = 0x1f;							      	\
    }									      		\
  else if ((Char & 0xf0) == 0xe0)					\
    {									      		\
      Len = 3;								      	\
      Mask = 0x0f;							      	\
    }									      		\
  else if ((Char & 0xf8) == 0xf0)			      	\
    {									      		\
      Len = 4;								      	\
      Mask = 0x07;							      	\
    }									      		\
  else if ((Char & 0xfc) == 0xf8)					\
    {									      		\
      Len = 5;								      	\
      Mask = 0x03;							      	\
    }												\
  else if ((Char & 0xfe) == 0xfc)					\
    {									      		\
      Len = 6;								      	\
      Mask = 0x01;							      	\
    }									      		\
  else									      		\
    Len = -1;

#define UTF8_LENGTH(Char)              				\
  ((Char) < 0x80 ? 1 :                 				\
   ((Char) < 0x800 ? 2 :               				\
    ((Char) < 0x10000 ? 3 :            				\
     ((Char) < 0x200000 ? 4 :          				\
      ((Char) < 0x4000000 ? 5 : 6)))))

#define UTF8_GET(Result, Chars, Count, Mask, Len)	\
  (Result) = (Chars)[0] & (Mask);					\
  for ((Count) = 1; (Count) < (Len); ++(Count))		\
    {									      		\
      if (((Chars)[(Count)] & 0xc0) != 0x80)		\
	{								      			\
	  (Result) = -1;						      	\
	  break;							      		\
	}								      			\
      (Result) <<= 6;							    \
      (Result) |= ((Chars)[(Count)] & 0x3f);		\
    }

#define UNICODE_VALID(Char)                   		\
    ((Char) < 0x110000 &&                     		\
     (((Char) & 0xFFFFF800) != 0xD800) &&     		\
     ((Char) < 0xFDD0 || (Char) > 0xFDEF) &&  		\
     ((Char) & 0xFFFE) != 0xFFFE)

static const char utf8_skip_data[256] = {
	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
	2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
	3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1
};

#define utf8_next_char(p) (char *)((p) + utf8_skip_data[*(const unsigned char *)(p)])

int unichar_to_utf8(wchar_t c, char *outbuf)
{
	unsigned int len = 0;
	int first;

	if (c < 0x80)
	{
		first = 0;
		len = 1;
	}
	else if (c < 0x800)
	{
		first = 0xc0;
		len = 2;
	}
	else if (c < 0x10000)
	{
		first = 0xe0;
		len = 3;
	}
	else if (c < 0x200000)
	{
		first = 0xf0;
		len = 4;
	}
	else if (c < 0x4000000)
	{
		first = 0xf8;
		len = 5;
	}
	else
	{
		first = 0xfc;
		len = 6;
	}

	if (outbuf)
	{
		for (int i = len - 1; i > 0; --i)
		{
			outbuf[i] = (c & 0x3f) | 0x80;
			c >>= 6;
		}
		outbuf[0] = c | first;
	}
	return len;
}

// First overload: stop on '\0' character.
static ustring::size_type utf8_byte_offset(const char* str,
		ustring::size_type offset)
{
	if (offset == ustring::npos)
		return ustring::npos;

	const char * const utf8_skip = utf8_skip_data;
	const char* p = str;

	for (; offset != 0; --offset)
	{
		const unsigned int c = static_cast<unsigned char> (*p);

		if (c == 0)
			return ustring::npos;

		p += utf8_skip[c];
	}

	return (p - str);
}

// Second overload: stop when reaching maxlen.
static ustring::size_type utf8_byte_offset(const char* str,
		ustring::size_type offset, ustring::size_type maxlen)
{
	if (offset == ustring::npos)
		return ustring::npos;

	const char * const utf8_skip = utf8_skip_data;
	const char * const pend = str + maxlen;
	const char* p = str;

	for (; offset != 0; --offset)
	{
		if (p >= pend)
			return ustring::npos;

		p += utf8_skip[static_cast<unsigned char> (*p)];
	}

	return (p - str);
}

// Third overload: stop when reaching str.size().
inline ustring::size_type utf8_byte_offset(const std::string& str,
		ustring::size_type offset)
{
	return utf8_byte_offset(str.data(), offset, str.size());
}


static long utf8_pointer_to_offset(const char* str, const char* pos)
{
	const char* s = str;
	long offset = 0;

	if (pos < str)
		offset = -utf8_pointer_to_offset(pos, str);
	else
		while (s < pos)
		{
			s = utf8_next_char(s);
			offset++;
		}
	return offset;
}

static char* utf8_offset_to_pointer(const char* str, long offset)
{
	const char *s = str;

	if (offset > 0)
		while (offset--)
			s = utf8_next_char(s);
	else
	{
		const char *s1;
		while (offset)
		{
			s1 = s;
			s += offset;
			while ((*s & 0xc0) == 0x80)
				s--;

			offset += utf8_pointer_to_offset(s, s1);
		}
	}
	return (char *) s;
}

// Converts byte offset to UTF-8 character offset.
inline ustring::size_type utf8_char_offset(const std::string& str,
		ustring::size_type offset)
{
	if (offset == ustring::npos)
		return ustring::npos;

	const char* const pdata = str.data();
	return utf8_pointer_to_offset(pdata, pdata + offset);
}

struct UnicharToUtf8
{
	char buf[6];
	ustring::size_type len;

	explicit UnicharToUtf8(wchar_t uc)
	: len(unichar_to_utf8(uc, buf))
	{
	}
};

struct Utf8SubstrBounds
{
	ustring::size_type i;
	ustring::size_type n;

	Utf8SubstrBounds(const std::string& str, ustring::size_type ci,
			ustring::size_type cn)
	: i(utf8_byte_offset(str, ci)), n(ustring::npos)
	{
		if (i != ustring::npos)
			n = utf8_byte_offset(str.data() + i, cn, str.size() - i);
	}
};


wchar_t unichar_from_utf8(const char* p, int* plen)
{
	int i, mask = 0, len;
	wchar_t result;
	unsigned char c = (unsigned char) *p;

	UTF8_COMPUTE (c, mask, len);
	if (plen)
		*plen = len;
	if (len == -1)
		return (wchar_t) -1;
	UTF8_GET (result, p, i, mask, len);
	return result;
}

/*********************** ustring ****************************/
ustring::ustring()
: _str()
{
}

ustring::ustring(const ustring & src)
: _str(src._str)
{
}

ustring::ustring(const ustring& src, ustring::size_type i, ustring::size_type n)
: _str()
{
	const Utf8SubstrBounds bounds (src._str, i, n);
	_str.assign(src._str, bounds.i, bounds.n);
}

ustring::ustring(const char* src)
: _str(src)
{
}

ustring::ustring(const char* src, size_type size)
: _str()
{
	const Utf8SubstrBounds bounds (src, 0, size);
	_str.assign(src, bounds.i, bounds.n);
}

ustring::ustring(const wchar_t* src)
: _str()
{
	wstring_to_utf8(src, _str);
}

ustring::ustring(const wchar_t* src, size_type size)
: _str()
{
	wstring_to_utf8(std::wstring(src, size), _str);
}

ustring::ustring(const std::string& src)
: _str(src)
{
}

ustring::ustring(const std::wstring& src)
: _str()
{
	wstring_to_utf8(src, _str);
}

ustring::ustring(size_type n, char c)
: _str(n, c)
{
}

ustring::ustring(size_type n, wchar_t uc)
: _str()
{
	if (uc < 0x80)
	{
		_str.assign(n, static_cast<char> (uc));
	}
	else
	{
		const UnicharToUtf8 conv(uc);
		_str.reserve(n * conv.len);

		for (; n > 0; --n)
			_str.append(conv.buf, conv.len);
	}
}

// Convert
ustring::operator const std::string& () const
{
	return _str;
}

ustring::operator std::string() const
{
	return _str;
}

ustring::operator std::wstring() const
{
	return utf8_to_wstring(_str);
}

const char* ustring::c_str() const
{
	return _str.c_str();
}

const char* ustring::data() const
{
	return _str.data();
}


// Assign
ustring& ustring::assign(const char* src, size_type n)
{
	_str.assign(src, utf8_byte_offset(src, n));
	return *this;
}

ustring& ustring::assign(const wchar_t* src, size_type n)
{
	wstring_to_utf8(std::wstring(src, n), _str);
	return *this;
}

ustring& ustring::assign(ustring::size_type n, wchar_t uc)
{
	ustring temp(n, uc);
	_str.swap(temp._str);
	return *this;
}

ustring& ustring::assign(ustring::size_type n, char c)
{
	_str.assign(n, c);
	return *this;
}

ustring& ustring::operator =(const ustring& src)
{
	_str = src._str;
	return *this;
}

ustring& ustring::operator =(const std::string& src)
{
	_str = src;
	return *this;
}

ustring& ustring::operator =(const std::wstring& src)
{
	wstring_to_utf8(src, _str);
	return *this;
}

ustring& ustring::operator =(const char* src)
{
	_str = src;
	return *this;
}

ustring& ustring::operator =(const wchar_t* src)
{
	wstring_to_utf8(src, _str);
	return *this;
}

ustring & ustring::operator =(const char c)
{
	_str = c;
	return *this;
}

ustring & ustring::operator =(const wchar_t uc)
{
	const UnicharToUtf8 conv(uc);
	_str.assign(conv.buf, conv.len);
	return *this;
}


// Append
ustring& ustring::append(const char* src, size_type n)
{
	_str.append(src, utf8_byte_offset(src, n));
	return *this;
}

ustring& ustring::append(const wchar_t* src, size_type n)
{
	_str.append(wstring_to_utf8(std::wstring(src, n)));
	return *this;
}

ustring& ustring::append(size_type n, const char c)
{
	_str.append(n, c);
	return *this;
}

ustring& ustring::append(size_type n, const wchar_t uc)
{
	_str.append(ustring(n, uc)._str);
	return *this;
}


ustring& ustring::operator +=(const ustring& other)
{
	_str += other._str;
	return *this;
}

ustring& ustring::operator +=(const std::string& other)
{
	_str += other;
	return *this;
}

ustring& ustring::operator +=(const std::wstring& other)
{
	_str += wstring_to_utf8(other);
	return *this;
}

ustring& ustring::operator +=(const char* other)
{
	_str += other;
	return *this;
}

ustring& ustring::operator +=(const wchar_t* other)
{
	_str += wstring_to_utf8(other);
	return *this;
}

ustring& ustring::operator +=(char c)
{
	_str += c;
	return *this;
}

ustring& ustring::operator +=(wchar_t uc)
{
	const UnicharToUtf8 conv (uc);
	_str.append(conv.buf, conv.len);
	return *this;
}

void ustring::push_back(char c)
{
	_str += c;
}

void ustring::push_back(wchar_t uc)
{
	const UnicharToUtf8 conv (uc);
	_str.append(conv.buf, conv.len);
}



// Insert
ustring& ustring::insert(size_type i, const ustring& src)
{
	_str.insert(utf8_byte_offset(_str, i), src._str);
	return *this;
}

ustring& ustring::insert(size_type i, const std::string& src)
{
	_str.insert(utf8_byte_offset(_str, i), src);
	return *this;
}

ustring& ustring::insert(size_type i, const char* src)
{
	_str.insert(utf8_byte_offset(_str, i), src);
	return *this;
}

ustring& ustring::insert(size_type i, const char* src, size_type n)
{
	_str.insert(utf8_byte_offset(_str, i), src, utf8_byte_offset(src, n));
	return *this;
}

ustring& ustring::insert(size_type i, const std::wstring& src)
{
	_str.insert(utf8_byte_offset(_str, i), wstring_to_utf8(src));
	return *this;
}

ustring& ustring::insert(size_type i, const wchar_t* src)
{
	_str.insert(utf8_byte_offset(_str, i), wstring_to_utf8(src));
	return *this;
}

ustring& ustring::insert(size_type i, const wchar_t* src, size_type n)
{
	_str.insert(utf8_byte_offset(_str, i), wstring_to_utf8(std::wstring(src, n)));
	return *this;
}

ustring& ustring::insert(size_type i, size_type n, const char c)
{
	_str.insert(utf8_byte_offset(_str, i), n, c);
	return *this;
}

ustring& ustring::insert(size_type i, size_type n, const wchar_t uc)
{
	_str.insert(utf8_byte_offset(_str, i), ustring(n, uc)._str);
	return *this;
}


// Compare
int ustring::compare(const ustring& src) const
{
	std::wstring wstr1, wstr2;
	utf8_to_wstring(_str, wstr1);
	utf8_to_wstring(src._str, wstr2);
	return wcscmp(wstr1.c_str(), wstr2.c_str());
}

int ustring::compare(const char* src) const
{
	std::wstring wstr1, wstr2;
	utf8_to_wstring(_str, wstr1);
	utf8_to_wstring(src, wstr2);
	return wcscmp(wstr1.c_str(), wstr2.c_str());
}

int ustring::compare(const wchar_t* src) const
{
	std::wstring wstr;
	utf8_to_wstring(_str, wstr);
	return wcscmp(wstr.c_str(), src);
}

int ustring::compare(const std::string& src) const
{
	std::wstring wstr1, wstr2;
	utf8_to_wstring(_str, wstr1);
	utf8_to_wstring(src, wstr2);
	return wcscmp(wstr1.c_str(), wstr2.c_str());
}

int ustring::compare(const std::wstring& src) const
{
	std::wstring wstr;
	utf8_to_wstring(_str, wstr);
	return wcscmp(wstr.c_str(), src.c_str());
}


// Erase
ustring& ustring::erase(size_type i, size_type n)
{
	const Utf8SubstrBounds bounds (_str, i, n);
	_str.erase(bounds.i, bounds.n);
	return *this;
}

ustring& ustring::erase()
{
	_str.erase();
	return *this;
}

void ustring::clear()
{
	_str.erase();
}


// Utilities
ustring::size_type ustring::find(const ustring& str, size_type i) const
{
	return utf8_char_offset(_str, _str.find(str._str, utf8_byte_offset(_str, i)));
}

ustring& ustring::replace(size_type i, size_type n, const ustring& src)
{
	const Utf8SubstrBounds bounds (_str, i, n);
	_str.replace(bounds.i, bounds.n, src._str);
	return *this;
}

ustring ustring::lowercase() const
{
	std::wstring temp;
	utf8_to_wstring(_str, temp);
	to_lower(temp);
	return temp;
}

ustring ustring::uppercase() const
{
	std::wstring temp;
	utf8_to_wstring(_str, temp);
	to_upper(temp);
	return temp;
}

wchar_t ustring::operator [](size_t i) const
{
	return unichar_from_utf8(utf8_offset_to_pointer(_str.data(), i));
}



// Store
bool ustring::empty() const
{
	return _str.empty();
}

ustring::size_type ustring::length() const
{
	const char *const pdata = _str.data();
	return utf8_pointer_to_offset(pdata, pdata + _str.size());
}

ustring::size_type ustring::size() const
{
	const char *const pdata = _str.data();
	return utf8_pointer_to_offset(pdata, pdata + _str.size());
}

ustring::size_type ustring::capacity() const
{
	return _str.capacity();
}

ustring::size_type ustring::bytes() const
{
	return _str.size();
}

ustring::size_type ustring::max_size() const
{
	return _str.max_size();
}

void ustring::reserve(size_type n)
{
	_str.reserve(n);
}

void ustring::resize(size_type n, char c)
{
	const size_type size_now = size();
	if (n < size_now)
		erase(n, npos);
	else if (n > size_now)
		_str.append(n - size_now, c);
}

void ustring::resize(size_type n, wchar_t uc)
{
	const size_type size_now = size();
	if (n < size_now)
		erase(n, npos);
	else if (n > size_now)
		append(n - size_now, uc);
}



codecvt_utf16_le::codecvt_utf16_le(size_t refs) :
	wchar_codecvt_base(refs)
{
}

codecvt_utf16_le::result
codecvt_utf16_le::do_out(state_type& state, const intern_type* from,
     const intern_type* from_end, const intern_type*& from_next,
     extern_type* to, extern_type* to_end,
     extern_type*& to_next) const
{
#ifdef __MSVC__
	return noconv;
#elif defined(__GNUC__)
	from_next = from;
	to_next = to;
	if (from_end - from_next > 1 && to_end - to_next < 2)
		return error;
	while (from_next < from_end && to_next < to_end - 1)
	{
		unsigned short wch = (unsigned short)(*from_next);
		*to_next++ = (char)(wch & 0xFF);
		*to_next++ = (char)((wch >> 8) & 0xFF);
		from_next++;
	}
	if (from_next < from_end)
		return partial;
	else
		return ok;
#else
#error "Not implement."
#endif
}

codecvt_utf16_le::result
codecvt_utf16_le::do_in(state_type& state, const extern_type* from,
    const extern_type* from_end, const extern_type*& from_next,
    intern_type* to, intern_type* to_end, intern_type*& to_next) const
{
#ifdef __MSVC__
	return noconv;
#elif defined(__GNUC__)
	from_next = from;
	to_next = to;
	if (from_end - from_next > 2 && to_end - to_next < 1)
		return error;
	while (from_next < from_end - 1 && to_next < to_end)
	{
		unsigned short wch = (unsigned char)*(from_next + 1);
		wch <<= 8;
		wch |= (unsigned char)*from_next;
		*to_next++ = wch;
		from_next += 2;
	}
	if (from_next < from_end)
		return partial;
	else
		return ok;
#else
#error "Not implement."
#endif
}

codecvt_utf16_le::result
codecvt_utf16_le::do_unshift(state_type& state, extern_type* to,
	 extern_type* to_end, extern_type*& to_next) const
{
	return noconv;
}

// Returns the width of an internal character in terms of external characters,
// if this is a fixed value.
// Otherwise, if this is a variable value, the function returns 0.
// Alternatively, if the encoding of an external sequence is state-dependent, the function returns -1.
int codecvt_utf16_le::do_encoding() const throw()
{
	return 2;
}

bool codecvt_utf16_le::do_always_noconv() const throw()
{
#ifdef __MSVC__
	// In windows wchar_t is same with utf16-le.
	return true;
#elif defined(__GNUC__)
	return false;
#else
#error "Not implement."
#endif
}

// Returns the amount of external characters in the range [from,from_end)
// that could be translated into at maximum max internal characters
int codecvt_utf16_le::do_length(state_type&, const extern_type* from,
	const extern_type* from_end, size_t max) const
{
	size_t size = (from_end - from) / 2;
	return size > max ? max : size;
}

// Returns the maximum amount of external characters needed for one internal character.
int codecvt_utf16_le::do_max_length() const throw()
{
	return 2;
}





codecvt_utf16_be::codecvt_utf16_be(size_t refs) :
	wchar_codecvt_base(refs)
{
}

codecvt_utf16_be::result
codecvt_utf16_be::do_out(state_type& state, const intern_type* from,
     const intern_type* from_end, const intern_type*& from_next,
     extern_type* to, extern_type* to_end,
     extern_type*& to_next) const
{
	from_next = from;
	to_next = to;
	if (from_end - from_next > 1 && to_end - to_next < 2)
		return error;
	while (from_next < from_end && to_next < to_end - 1)
	{
		unsigned short wch = (unsigned short)(*from_next);
		*to_next++ = (char)((wch >> 8) & 0xFF);
		*to_next++ = (char)(wch & 0xFF);
		from_next++;
	}
	if (from_next < from_end)
		return partial;
	else
		return ok;
}

codecvt_utf16_be::result
codecvt_utf16_be::do_in(state_type& state, const extern_type* from,
    const extern_type* from_end, const extern_type*& from_next,
    intern_type* to, intern_type* to_end, intern_type*& to_next) const
{
	from_next = from;
	to_next = to;
	if (from_end - from_next > 2 && to_end - to_next < 1)
		return error;
	while (from_next < from_end - 1 && to_next < to_end)
	{
		unsigned short wch = (unsigned char)*(from_next);
		wch <<= 8;
		wch |= (unsigned char)*(from_next + 1);
		*to_next++ = wch;
		from_next += 2;
	}
	if (from_next < from_end)
		return partial;
	else
		return ok;
}

codecvt_utf16_be::result
codecvt_utf16_be::do_unshift(state_type& state, extern_type* to,
	 extern_type* to_end, extern_type*& to_next) const
{
	return noconv;
}

// Returns the width of an internal character in terms of external characters,
// if this is a fixed value.
// Otherwise, if this is a variable value, the function returns 0.
// Alternatively, if the encoding of an external sequence is state-dependent, the function returns -1.
int codecvt_utf16_be::do_encoding() const throw()
{
	return 2;
}

bool codecvt_utf16_be::do_always_noconv() const throw()
{
	return false;
}

// Returns the amount of external characters in the range [from,from_end)
// that could be translated into at maximum max internal characters
int codecvt_utf16_be::do_length(state_type&, const extern_type* from,
	const extern_type* from_end, size_t max) const
{
	size_t size = (from_end - from) / 2;
	return size > max ? max : size;
}

// Returns the maximum amount of external characters needed for one internal character.
int codecvt_utf16_be::do_max_length() const throw()
{
	return 2;
}






codecvt_char_utf16_le::codecvt_char_utf16_le(size_t refs) :
	char_codecvt_base(refs)
{
}

codecvt_char_utf16_le::result
codecvt_char_utf16_le::do_out(state_type& state, const intern_type* from,
     const intern_type* from_end, const intern_type*& from_next,
     extern_type* to, extern_type* to_end,
     extern_type*& to_next) const
{
	from_next = from;
	to_next = to;

	int len = 0;
	while (from_next < from_end && to_next < to_end - 1)
	{
		unsigned short wch = (unsigned short)unichar_from_utf8(from_next, &len);
		if (len <= 0)
			return error;
		from_next += len;
		*to_next++ = (char)(wch & 0xFF);
		*to_next++ = (char)((wch >> 8) & 0xFF);
	}
	if (from_next < from_end)
		return partial;
	else
		return ok;
}

codecvt_char_utf16_le::result
codecvt_char_utf16_le::do_in(state_type& state, const extern_type* from,
    const extern_type* from_end, const extern_type*& from_next,
    intern_type* to, intern_type* to_end, intern_type*& to_next) const
{
	from_next = from;
	to_next = to;

	size_t len = 0;
	if (from_end - from_next < 2)
		return error;
	while (from_next < from_end - 1 && to_next + UTF8_LENGTH(*from_next) <= to_end)
	{
		wchar_t tmp = (unsigned char)*(from_next + 1);
		tmp <<= 8;
		tmp |= (unsigned char)*from_next;
		from_next += 2;
		len = unichar_to_utf8(tmp, to_next);
		to_next += len;
	}
	if (from_next < from_end)
		return partial;
	else
		return ok;
}

codecvt_char_utf16_le::result
codecvt_char_utf16_le::do_unshift(state_type& state, extern_type* to,
	 extern_type* to_end, extern_type*& to_next) const
{
	return noconv;
}

// Returns the width of an internal character in terms of external characters,
// if this is a fixed value.
// Otherwise, if this is a variable value, the function returns 0.
// Alternatively, if the encoding of an external sequence is state-dependent, the function returns -1.
int codecvt_char_utf16_le::do_encoding() const throw()
{
	return 2;
}

bool codecvt_char_utf16_le::do_always_noconv() const throw()
{
	return false;
}

// Returns the amount of external characters in the range [from,from_end)
// that could be translated into at maximum max internal characters
int codecvt_char_utf16_le::do_length(state_type&, const extern_type* from,
	const extern_type* from_end, size_t max) const
{
	size_t len = 0;
	while (from < from_end - 1)
	{
		wchar_t tmp = (unsigned char)*(from + 1);
		tmp <<= 8;
		tmp |= (unsigned char)*from;
		from += 2;
		len += UTF8_LENGTH(tmp);
	}
	return len > max ? max : len;
}

// Returns the maximum amount of external characters needed for one internal character.
int codecvt_char_utf16_le::do_max_length() const throw()
{
	return 2;
}




codecvt_char_utf16_be::codecvt_char_utf16_be(size_t refs) :
	char_codecvt_base(refs)
{
}

codecvt_char_utf16_be::result
codecvt_char_utf16_be::do_out(state_type& state, const intern_type* from,
     const intern_type* from_end, const intern_type*& from_next,
     extern_type* to, extern_type* to_end,
     extern_type*& to_next) const
{
	from_next = from;
	to_next = to;

	int len = 0;
	while (from_next < from_end && to_next < to_end - 1)
	{
		unsigned short wch = (unsigned short)unichar_from_utf8(from_next, &len);
		if (len <= 0)
			return error;
		from_next += len;

		// BE or LE different
		*to_next++ = (char)((wch >> 8) & 0xFF);
		*to_next++ = (char)(wch & 0xFF);
	}
	if (from_next < from_end)
		return partial;
	else
		return ok;
}

codecvt_char_utf16_be::result
codecvt_char_utf16_be::do_in(state_type& state, const extern_type* from,
    const extern_type* from_end, const extern_type*& from_next,
    intern_type* to, intern_type* to_end, intern_type*& to_next) const
{
	from_next = from;
	to_next = to;

	size_t len = 0;
	if (from_end - from_next < 2)
		return error;
	while (from_next < from_end - 1 && to_next + UTF8_LENGTH(*from_next) <= to_end)
	{
		wchar_t tmp = (unsigned char)*from_next;
		tmp <<= 8;
		tmp |= (unsigned char)*(from_next + 1);
		from_next += 2;
		len = unichar_to_utf8(tmp, to_next);
		to_next += len;
	}
	if (from_next < from_end)
		return partial;
	else
		return ok;
}

codecvt_char_utf16_be::result
codecvt_char_utf16_be::do_unshift(state_type& state, extern_type* to,
	 extern_type* to_end, extern_type*& to_next) const
{
	return noconv;
}

// Returns the width of an internal character in terms of external characters,
// if this is a fixed value.
// Otherwise, if this is a variable value, the function returns 0.
// Alternatively, if the encoding of an external sequence is state-dependent, the function returns -1.
int codecvt_char_utf16_be::do_encoding() const throw()
{
	return 2;
}

bool codecvt_char_utf16_be::do_always_noconv() const throw()
{
	return false;
}

// Returns the amount of external characters in the range [from,from_end)
// that could be translated into at maximum max internal characters
int codecvt_char_utf16_be::do_length(state_type&, const extern_type* from,
	const extern_type* from_end, size_t max) const
{
	size_t len = 0;
	while (from < from_end - 1)
	{
		wchar_t tmp = (unsigned char)*from;
		tmp <<= 8;
		tmp |= (unsigned char)*(from + 1);
		from += 2;
		len += UTF8_LENGTH(tmp);
	}
	return len > max ? max : len;
}

// Returns the maximum amount of external characters needed for one internal character.
int codecvt_char_utf16_be::do_max_length() const throw()
{
	return 2;
}


} // End of namespace tlib


